package com.radicaldynamic.gcmobile.android.build; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.UUID; import android.app.AlertDialog; import android.app.Dialog; import android.app.ExpandableListActivity; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Resources; import android.graphics.Typeface; import android.os.Bundle; import android.text.TextUtils.TruncateAt; import android.view.ContextMenu; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.ContextMenu.ContextMenuInfo; import android.widget.BaseExpandableListAdapter; import android.widget.EditText; import android.widget.ExpandableListView; import android.widget.TextView; import android.widget.Toast; import android.widget.ExpandableListView.ExpandableListContextMenuInfo; import com.radicaldynamic.groupinform.R; import com.radicaldynamic.groupinform.application.Collect; import com.radicaldynamic.groupinform.utilities.TranslationSortByLang; import com.radicaldynamic.groupinform.xform.Translation; public class I18nList extends ExpandableListActivity { private static final int DIALOG_EDIT_TRANSLATION = 0; private static final int DIALOG_RESET_TRANSLATIONS = 1; private static final int DIALOG_REMOVE_LANGUAGE = 2; private static final int DIALOG_REMOVE_TRANSLATION = 3; private static final int DIALOG_TRANSLATIONS_EXIST = 4; // Show translations matching this key/this translation ID was selected public static final String KEY_TRANSLATION_ID = "key_translation_id"; public static final String KEY_FIELDTEXT_TYPE = "key_fieldtext_type"; // Either "label" or "description" public static final String KEY_DESCRIPTION = "description"; public static final String KEY_LABEL = "label"; public static final String KEY_ITEM_LABEL = "the item label"; private static final String KEY_SELECT_SINGLE_MODE = "key_select_single_translation"; private static final String KEY_GROUP_POSITION = "key_group_position"; private static final String KEY_CHILD_POSITION = "key_child_position"; private BaseExpandableListAdapter mAdapter; private LayoutInflater mInflater; private Boolean mSelectSingleMode = false; private String mTranslationId = ""; // Filter translations by this ID private String mFieldTextType = ""; private ArrayList<Translation> mTranslations = Collect.getInstance().getFormBuilderState().getTranslations(); private ArrayList<String> mAbbreviations = new ArrayList<String>(); private String [] mLanguages; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mAdapter = new LanguageListAdapter(); mInflater = (LayoutInflater) getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); // Load reference lists Resources res = getResources(); mLanguages = res.getStringArray(R.array.i18n); mAbbreviations = new ArrayList<String>(Arrays.asList(res.getStringArray(R.array.i18n_abbrev))); // Sort translation list Collections.sort(mTranslations, new TranslationSortByLang(mLanguages, mAbbreviations)); if (savedInstanceState == null) { Intent i = getIntent(); if (i.hasExtra(KEY_FIELDTEXT_TYPE)) mFieldTextType = i.getStringExtra(KEY_FIELDTEXT_TYPE); if (i.hasExtra(KEY_TRANSLATION_ID)) { mSelectSingleMode = true; mTranslationId = i.getStringExtra(KEY_TRANSLATION_ID); } } else { if (savedInstanceState.containsKey(KEY_FIELDTEXT_TYPE)) mFieldTextType = savedInstanceState.getString(KEY_FIELDTEXT_TYPE); if (savedInstanceState.containsKey(KEY_SELECT_SINGLE_MODE)) mSelectSingleMode = savedInstanceState.getBoolean(KEY_SELECT_SINGLE_MODE); if (savedInstanceState.containsKey(KEY_TRANSLATION_ID)) mTranslationId = savedInstanceState.getString(KEY_TRANSLATION_ID); } // Provide a title specific to the context in which this activity is used if (mSelectSingleMode) setTitle(getString(R.string.app_name) + " > " + getString(R.string.tf_i18n_translate)); else setTitle(getString(R.string.app_name) + " > " + getString(R.string.tf_i18n_setup)); setListAdapter(mAdapter); getExpandableListView().setOnChildClickListener(this); registerForContextMenu(getExpandableListView()); } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuInfo; int type = ExpandableListView.getPackedPositionType(info.packedPosition); if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.formbuilderi18nlist_cmenu, menu); } } @Override public Dialog onCreateDialog(int id, final Bundle args) { AlertDialog.Builder builder = new AlertDialog.Builder(this); Dialog dialog = null; LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = null; switch (id) { case DIALOG_EDIT_TRANSLATION: // Retrieve language final Translation translationGroup = mTranslations.get(args.getInt(KEY_GROUP_POSITION)); Translation translationText = new Translation(mTranslationId, null); String language = Translation.expandLangAbbreviation(mLanguages, mAbbreviations, translationGroup.getLang()); /* * Find specific translation (unfortunately we cannot use child position because we * fooled the adapter into thinking that there was only one child when in reality * there may be many) */ if (translationGroup.getTexts().isEmpty()) { // Adding new translation } else { Iterator<Translation> it = translationGroup.getTexts().iterator(); while (it.hasNext()) { Translation t = it.next(); if (t.getId().equals(mTranslationId)) { translationText = t; } } } view = inflater.inflate(R.layout.dialog_edit_translation, null); // Set an EditText view to get user input final EditText copy = (EditText) view.findViewById(R.id.copy); copy.setText(translationText.toString()); builder.setView(view); builder.setInverseBackgroundForced(true); builder.setTitle(getString(R.string.tf_i18n_edit_translation, language)); builder.setPositiveButton(getText(R.string.ok), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { if (copy.getText().toString().trim().length() < 1) { Toast.makeText(I18nList.this, getString(R.string.tf_unable_to_save_empty_translation), Toast.LENGTH_SHORT).show(); removeDialog(DIALOG_EDIT_TRANSLATION); showDialog(DIALOG_EDIT_TRANSLATION, args); } else { Iterator<Translation> it = translationGroup.getTexts().iterator(); boolean textUpdated = false; while (it.hasNext()) { Translation t = it.next(); if (t.getId().equals(mTranslationId)) { t.setValue(copy.getText().toString().trim()); textUpdated = true; break; } } // Add new translation if an existing text could not be found to be updated if (!textUpdated) { /* * If a translation ID was not passed then this means that the label/description * has not yet been translated so we initialize it appropriately here */ if (mTranslationId == null) { mTranslationId = UUID.randomUUID().toString().replaceAll("[^a-zA-Z0-9]", ""); String jr = "jr:itext('" + mTranslationId + "')"; if (mFieldTextType.equals(KEY_ITEM_LABEL)) { Collect.getInstance().getFormBuilderState().getItem().setLabel(jr); } else if (mFieldTextType.equals(KEY_LABEL)) { Collect.getInstance().getFormBuilderState().getField().setLabel(jr); } else if (mFieldTextType.equals(KEY_DESCRIPTION)) { Collect.getInstance().getFormBuilderState().getField().setHint(jr); } } translationGroup.getTexts().add(new Translation(mTranslationId, copy.getText().toString().trim())); } removeDialog(DIALOG_EDIT_TRANSLATION); refreshView(); } } }); builder.setNeutralButton(getText(R.string.cancel), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { removeDialog(DIALOG_EDIT_TRANSLATION); } }); if (mSelectSingleMode && translationText.getValue() != null) { builder.setNegativeButton(getText(R.string.tf_remove), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { removeDialog(DIALOG_EDIT_TRANSLATION); showDialog(DIALOG_REMOVE_TRANSLATION, args); } }); } dialog = builder.create(); break; case DIALOG_RESET_TRANSLATIONS: builder.setMessage(getString(R.string.tf_i18n_reset_translations_dialog_msg, mFieldTextType)); builder.setTitle(getString(R.string.tf_i18n_reset_translations_dialog)); builder.setPositiveButton(getText(R.string.tf_i18n_reset_translations), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { Iterator<Translation> it = mTranslations.iterator(); while (it.hasNext()) { Translation t = it.next(); Iterator<Translation> itt = t.getTexts().iterator(); while (itt.hasNext()) { Translation tt = itt.next(); if (tt.getId().equals(mTranslationId)) itt.remove(); } } // Mark the current field label or description as being blank/untranslated if (mFieldTextType.equals(KEY_ITEM_LABEL)) { Collect.getInstance().getFormBuilderState().getItem().setLabel(""); } else if (mFieldTextType.equals(KEY_LABEL)) { Collect.getInstance().getFormBuilderState().getField().setLabel(""); } else if (mFieldTextType.equals(KEY_DESCRIPTION)) { Collect.getInstance().getFormBuilderState().getField().setHint(""); } Toast.makeText(I18nList.this, getString(R.string.tf_i18n_translations_reset, mFieldTextType), Toast.LENGTH_SHORT).show(); setResult(RESULT_OK); finish(); } }); builder.setNegativeButton(getText(R.string.cancel), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { removeDialog(DIALOG_RESET_TRANSLATIONS); } }); dialog = builder.create(); break; case DIALOG_REMOVE_LANGUAGE: // Retrieve language final Translation tgrl = mTranslations.get(args.getInt(KEY_GROUP_POSITION)); String lrl = Translation.expandLangAbbreviation(mLanguages, mAbbreviations, tgrl.getLang()); builder.setTitle(getString(R.string.tf_i18n_remove_language_dialog, lrl)); builder.setMessage(getString(R.string.tf_i18n_remove_language_dialog_msg, lrl)); builder.setPositiveButton(getText(R.string.tf_remove), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { if (tgrl.getTexts().isEmpty()) { // Bah. Aagain? String l = Translation.expandLangAbbreviation(mLanguages, mAbbreviations, tgrl.getLang()); Toast.makeText(I18nList.this, getString(R.string.tf_removed_with_param, l), Toast.LENGTH_SHORT).show(); mTranslations.remove(args.getInt(KEY_GROUP_POSITION)); removeDialog(DIALOG_REMOVE_LANGUAGE); refreshView(); } else { removeDialog(DIALOG_REMOVE_LANGUAGE); showDialog(DIALOG_TRANSLATIONS_EXIST); } } }); builder.setNegativeButton(getText(R.string.cancel), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { removeDialog(DIALOG_REMOVE_LANGUAGE); } }); dialog = builder.create(); break; case DIALOG_REMOVE_TRANSLATION: // Retrieve language final Translation tgrt = mTranslations.get(args.getInt(KEY_GROUP_POSITION)); String lrt = Translation.expandLangAbbreviation(mLanguages, mAbbreviations, tgrt.getLang()); builder.setTitle(getString(R.string.tf_i18n_remove_translation, lrt)); builder.setPositiveButton(getText(R.string.tf_remove), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // Again? Bah. String l = Translation.expandLangAbbreviation(mLanguages, mAbbreviations, tgrt.getLang()); // Find and nullify entry (nulled entries will not be written to XML) Iterator<Translation> it = tgrt.getTexts().iterator(); while (it.hasNext()) { Translation t = it.next(); if (t.getId().equals(mTranslationId)) { it.remove(); break; } } Toast.makeText(I18nList.this, getString(R.string.tf_i18n_translation_removed, l), Toast.LENGTH_SHORT).show(); removeDialog(DIALOG_REMOVE_TRANSLATION); refreshView(); } }); builder.setNegativeButton(getText(R.string.cancel), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { removeDialog(DIALOG_REMOVE_TRANSLATION); } }); dialog = builder.create(); break; case DIALOG_TRANSLATIONS_EXIST: builder.setTitle(getString(R.string.tf_unable_to_remove_language_dialog)); builder.setMessage(getString(R.string.tf_unable_to_remove_language_dialog_msg)); builder.setPositiveButton(getText(R.string.ok), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { dialog.cancel(); } }); dialog = builder.create(); break; } return dialog; } @Override @SuppressWarnings("unused") public boolean onContextItemSelected(MenuItem item) { ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) item.getMenuInfo(); // String title = ((TextView) info.targetView).getText().toString(); int type = ExpandableListView.getPackedPositionType(info.packedPosition); if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) { int groupPos = ExpandableListView.getPackedPositionGroup(info.packedPosition); int childPos = ExpandableListView.getPackedPositionChild(info.packedPosition); return true; } else if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) { int groupPos = ExpandableListView.getPackedPositionGroup(info.packedPosition); switch (item.getItemId()) { case R.id.removeLanguage: Bundle b = new Bundle(); b.putInt(KEY_GROUP_POSITION, groupPos); showDialog(DIALOG_REMOVE_LANGUAGE, b); break; case R.id.makeDefault: for (int i = 0; i < mTranslations.size(); i++) { if (i == groupPos) mTranslations.get(i).setFallback(true); else mTranslations.get(i).setFallback(false); } refreshView(); } return true; } return false; } @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.formbuilderi18nlist_omenu, menu); // "Remove all" translations isn't available unless a specific translation has been selected if (!mSelectSingleMode) menu.removeItem(R.id.remove); return true; } @Override public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { if (!mSelectSingleMode) mTranslationId = Collect.getInstance().getFormBuilderState().getTranslations() .get(groupPosition).getTexts() .get(childPosition).getId(); Bundle b = new Bundle(); b.putInt(KEY_GROUP_POSITION, groupPosition); b.putInt(KEY_CHILD_POSITION, childPosition); showDialog(DIALOG_EDIT_TRANSLATION, b); return false; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_BACK: setResult(RESULT_OK); finish(); return true; } return super.onKeyDown(keyCode, event); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.add: Intent i = new Intent(this, LanguageList.class); startActivity(i); break; case R.id.remove: showDialog(DIALOG_RESET_TRANSLATIONS); break; } return super.onOptionsItemSelected(item); } @Override protected void onResume() { super.onResume(); refreshView(); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString(KEY_FIELDTEXT_TYPE, mFieldTextType); outState.putBoolean(KEY_SELECT_SINGLE_MODE, mSelectSingleMode); outState.putString(KEY_TRANSLATION_ID, mTranslationId); } /** * A simple adapter which maintains an ArrayList of photo resource Ids. * Each photo is displayed as an image. This adapter supports clearing the * list of photos and adding a new photo. * */ private class LanguageListAdapter extends BaseExpandableListAdapter { public Object getChild(int groupPosition, int childPosition) { if (mSelectSingleMode) { Iterator<Translation> it = mTranslations.get(groupPosition).getTexts().iterator(); Translation d = new Translation(mTranslationId, "[Translation Missing]"); while (it.hasNext()) { Translation t = it.next(); if (t.getId().equals(mTranslationId) && t.getValue() instanceof String && t.getValue().length() > 0) { return t; } } return d; } else { return mTranslations.get(groupPosition).getTexts().get(childPosition); } } public long getChildId(int groupPosition, int childPosition) { return childPosition; } public int getChildrenCount(int groupPosition) { if (mSelectSingleMode) { return 1; } else { return mTranslations.get(groupPosition).getTexts().size(); } } public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { View v = mInflater.inflate(android.R.layout.simple_expandable_list_item_2, parent, false); TextView t = (TextView) v.findViewById(android.R.id.text1); t.setEllipsize(TruncateAt.END); t.setSingleLine(true); t.setText(getChild(groupPosition, childPosition).toString()); t.setTextColor(R.color.solid_black); t = (TextView) v.findViewById(android.R.id.text2); t.setTextColor(R.color.solid_black); // Context specific hints (removal only makes sense from the perspective of a field-specific translation) if (mSelectSingleMode) t.setText("Select to change or remove"); else t.setText("Select to change"); return v; } public Object getGroup(int groupPosition) { return mTranslations.get(groupPosition); } public int getGroupCount() { return mTranslations.size(); } public long getGroupId(int groupPosition) { return groupPosition; } public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { View v = mInflater.inflate(android.R.layout.simple_expandable_list_item_1, parent, false); TextView t = (TextView) v.findViewById(android.R.id.text1); t.setTextColor(R.color.solid_black); // Expand any abbreviations for the sake of readability if (mAbbreviations.contains(getGroup(groupPosition).toString().toLowerCase())) { int i = mAbbreviations.indexOf(getGroup(groupPosition).toString().toLowerCase()); t.setText(mLanguages[i].toString()); } else { t.setText(getGroup(groupPosition).toString()); } // Identify default/fallback translation if (mTranslations.get(groupPosition).isFallback()) { t.setTypeface(t.getTypeface(), Typeface.BOLD); t.setText(t.getText() + " (Default)"); } return v; } public boolean isChildSelectable(int groupPosition, int childPosition) { return true; } public boolean hasStableIds() { return true; } } private void refreshView() { // User may have added a new language, refresh to display if (mAdapter instanceof BaseExpandableListAdapter) { Collections.sort(mTranslations, new TranslationSortByLang(mLanguages, mAbbreviations)); mAdapter.notifyDataSetChanged(); } if (mTranslations.isEmpty()) { Toast.makeText(I18nList.this, getString(R.string.tf_add_language), Toast.LENGTH_LONG).show(); } } }